如何从文本中提取特征信息?
虽然之前也写过gensim库的word2vec的教程,但是对于文本分析的特征提取并不太理解。最近看了几篇scikit的外文教程,对机器学习中文本的特征提取有了一些了解。
首先做文本的机器学习(自然语言处理),我们要理解这几个概念:
文档(document)这里是指一段单独的文本信息。这可能是一则短信、一条推特、一封邮件、一本书、或者一首歌词。一般一个文档对应于一个观测值或一行数据。
语料(corpus) 文档的集合(语料大于等于1条文档)。这相当于我们要研究对象的所有文本数据
单词或词语(token) 英文中对应的是单词,汉语是词语。例如“How are you”这个文档,是由how、are、you三个单词组成的。token相当于机器学习中的特征(列)。
首先让我们给出一个语料例子。
corpus = ["Hey hey hey lets go get lunch today :)",
"Did you go home?",
"Hey!!! I need a favor"]
CountVectorizer
首先我们要明白,计算机是不能从文本字符串中发现规律的。只有将字符串编码为计算机可以理解的数字,计算机才有可能发现文本中的规律。
最容易理解的实现思路:
对文本编码,就是让词语与数字对应起来,建立基于给定文本的词典。(fit方法 )
再根据词典对所有的文本数据进行转码。(transform方法)
scikit库的CountVectorize类就是这种思路。
from sklearn.feature_extraction.text import CountVectorizer
vectorize = CountVectorizer()
使用fit方法,CountVectorizer()类的会从corpus语料中学习到所有词语,进而构建出corpus词典。
#fit学会语料中的所有词语,构建词典
vectorize.fit(corpus)
CountVectorizer(analyzer='word', binary=False, decode_error='strict',
dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
lowercase=True, max_df=1.0, max_features=None, min_df=1,
ngram_range=(1, 1), preprocessor=None, stop_words=None,
strip_accents=None, token_pattern='(?u)\\b\\w\\w+\\b',
tokenizer=None, vocabulary=None)
#这里我们查看下“词典”,也就是特征集(11个特征词)
vectorize.get_feature_names()
['did',
'favor',
'get',
'go',
'hey',
'home',
'lets',
'lunch',
'need',
'today',
'you']
这里,特征集(get_feature——name)返回的结果,有要注意的事情:
所有的单词都是小写
单词长度小于两个字母的,会被剔除掉
标点符号会剔除掉
不重复
这个特征集是有顺序的
接下来会按照这个顺序去编码文本数据,这里会出现一个新概念文档-词频矩阵(document-term matrix),英文简写为dtm。我们让机器挖掘文本中的规律时,喂给机器的数据就是 这文档-词频矩阵(document-term matrix)。
#构建 文档词频矩阵
dtm = vectorize.transform(corpus)dtm
<3x11 sparse matrix of type '<class 'numpy.int64'>'
with 13 stored elements in Compressed Sparse Row format>
构建文档词频矩阵。结果3*11。
3指的是语料中的三个文档;11指的是corpus中11个特征词。为了方便我们理解,我用pandas库将其展现出来
import pandas as pd
pd.DataFrame(dtm.toarray(), columns = vectorize.get_feature_names())
从上面的dataframe表中,行代表一个文档,列代表特征词。比如第1行,hey列的所对应的单元格值为3,说明corpus中第一个document(Hey hey hey lets go get lunch today :) 出现了三次hey。
这里有一个我要提示的重点,在此时我们已经构件号了文档词频矩阵dtm,如果你还想加入新的文档。我们应该注意tranform和fit的区别。
new_document = ['Hey lets go get a drink tonight']
new_dtm = vectorize.transform(new_document)
pd.DataFrame(new_dtm.toarray(), columns=vectorize.get_feature_names())
即使new_document含有6个单词,但是在上面的dataframe表中只有4个特征词被有效编码,drink和tonight词未被表征。这是因为我们初识的corpus语料所构建的词典并未含有这些词。但是对文本进行特征表征时,使用的确实corpus所生产的词典。
我们机器学习所用的数据,一般被分成训练集和测试集。训练集是为了让机器学习数据的规律,测试集是为了验证规律的有效性。训练集本质上代表的是过去及现在已知的数据,测试集本质上代表的是未来的未知数据(现在不存在的数据),我们是用已知的数据预测未来。
所以我们只能让fit方法操作于训练集,构建基于过去或已知数据的特征集。
TfidfVectorizer
scikit库除了CountVectorizer类,还有TfidfVectorizer类。TF-IDF这个定义相信大家应该已经耳熟能详了:
TF 词频出现的次数 IDF = 1/(语料中含有该词语的文档的数目)
TF简单点说有的时候词语出现越多,这个词越是特征词。但同时,有些“的它呢”等无意义词出现的多并没有什么意义,反而是像“核能”这种词,虽然不怎么出现,但是一出现往往很具有特征性。
综合TF*IDF,就能很好的刻画一个词语是否具有表征文本信息能力,作为特征是否合适。有了上面的基础知识,我们接下来继续写代码,便于理解TFIDF
from sklearn.feature_extraction.text import TfidfVectorizer
def createDTM(corpus):
"""构建文档词语矩阵"""
vectorize = TfidfVectorizer()
#注意fit_transform相当于fit之后又transform。
dtm = vectorize.fit_transform(corpus)
#打印dtm
return pd.DataFrame(dtm.toarray(), columns=vectorize.get_feature_names())
corpus = ["Hey lets get lunch :)",
"Hey!!! I need a favor"]
createDTM(corpus)
我们看到hey这个词在所有大于0的值里面,是最小的。因为hey同时出现在两个文档中,不具有文本的表征能力,所以TFIDF小。而favor和need因为他们仅仅出现在第二个文档中,所以他们的TFIDF值更高。
现在我们改变corpus内容。将第一个文档从Hey lets get lunch改为Hey hey hey lets get lunch。我们希望第一个文档的hey词频增大,进而使得其TFIDF值变大。
corpus = ["Hey hey hey lets get lunch :)",
"Hey!!! I need a favor"]
createDTM(corpus)
我们发现第一个文档hey的TFIDF值变大,但这里有两点要注意:
第一个文档中其余特征词的TFIDF值变小了
第二个文档中hey的TFIDF未发生变化
上面的例子是通过TF词频操作TFIDF值的。下面我们从IDF出发,改变TFIDF值。
corpus = ["Hey hey hey lets get lunch :)",
"I need a favor"]
createDTM(corpus)
由于hey只存在于文档一,且其词频是最多的,所有文档一的hey的TFIDF值最大。